#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <libelf.h>
#include <gelf.h>
#include <dlfcn.h>

#include "gotplt.h"

static int hexdump(const unsigned char *ptr, const int len)
{
    int i;

    printf("[YMK DUMP ptr 0x%08x, len 0x%08x]", ptr, len);
    for (i = 0; i < len; i++) {
        if (!(i % 16))
            printf("\n[0x%08x]", ptr + i);
        if (!(i % 8))
            printf(" ");
        printf("%02X ", *(ptr + i));
    }
    printf("\n");

    return 0;
}

static size_t find_section_by_string(Elf *e, const char *match)
{
    char *name;
    size_t shstrndx;
    Elf_Scn *scn;
    GElf_Shdr shdr;

    if (elf_getshdrstrndx(e, &shstrndx) != 0)
        errx(EXIT_FAILURE, "elf_getshdrstrndx() failed: %s", elf_errmsg(-1));

    scn = NULL;

    while ((scn = elf_nextscn(e, scn)) != NULL) {
        if (gelf_getshdr(scn ,&shdr) != &shdr)
            errx(EXIT_FAILURE, "getshdr() failed: %s.", elf_errmsg(-1));

        if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL)
            errx(EXIT_FAILURE, "elf_strptr() failed: %s.", elf_errmsg (-1));

        if (strncmp(name, match, strlen(match)) != 0)
            continue;

        /*printf("Section %-4.4jd %-20s ADDR 0x%08X OFST 0x%08X TYPE %-20s\n",
            (uintmax_t)elf_ndxscn(scn) , name, shdr.sh_addr, shdr.sh_offset,
                get_section_type_name(shdr.sh_type));*/
        return elf_ndxscn(scn);
    }
    return -1;
}

static int dump_gotplt(Elf *e)
{
    char relastr[] = ".rela.plt";
    char symtabstr[] = ".dynsym";
    size_t scnindex;
    size_t strtabindex;

    Elf_Scn *scn;
    Elf_Data *rela_data;
    Elf_Data *symtab_data;

    GElf_Shdr shdr;
    GElf_Rela rela;
    GElf_Sym sym;

    int i, count, symindex;

    /* find symtab */
    scnindex = find_section_by_string(e, symtabstr);
    if (scnindex < 0)
        errx(EXIT_FAILURE, "could not find section %s.", symtabstr);

    scn = elf_getscn(e, scnindex);
    if (scn == NULL)
        errx(EXIT_FAILURE, "could not get section %s for index %d.", symtabstr, scnindex);

    if (gelf_getshdr(scn ,&shdr) != &shdr)
        errx(EXIT_FAILURE, "getshdr() failed: %s.", elf_errmsg(-1));

    symtab_data = elf_getdata(scn, NULL);
    strtabindex = shdr.sh_link;
    //printf("[YMK strtab %d %s]\n", strtabindex, elf_strptr(e, strtabindex, 110));

    /* find rela */
    scnindex = find_section_by_string(e, relastr);
    if (scnindex < 0)
        errx(EXIT_FAILURE, "could not find section %s.", relastr);

    scn = elf_getscn(e, scnindex);
    if (scn == NULL)
        errx(EXIT_FAILURE, "could not get section %s for index %d.", relastr, scnindex);

    if (gelf_getshdr(scn ,&shdr) != &shdr)
        errx(EXIT_FAILURE, "getshdr() failed: %s.", elf_errmsg(-1));

    rela_data = elf_getdata(scn, NULL);
    count = shdr.sh_size/shdr.sh_entsize;

    for (i = 0; i < count; i++) {
        if (gelf_getrela(rela_data, i, &rela) != NULL) {
            symindex = GELF_R_SYM(rela.r_info);
            gelf_getsym(symtab_data, symindex, &sym);

            printf("[YMK find %s at 0x%08x value %p]\n",
                    elf_strptr(e, strtabindex, sym.st_name), rela.r_offset, *((void **)rela.r_offset));
#if 0
            printf("\t\tOFST 0x%08X INFO 0x%016lX %-20s\n",
                rela.r_offset, rela.r_info, elf_strptr(e, strtabindex, sym.st_name));
#endif
        }
    }

    return 0;
}

int dump_functions(void)
{
    int fd;
    Elf *e;
    Elf_Kind ek;

    /* check elf to make sure .got.plt is right */
    if (elf_version(EV_CURRENT) == EV_NONE)
        errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1));

    if ((fd = open("/proc/self/exe", O_RDONLY, 0)) < 0)
        errx(EXIT_FAILURE, "open /proc/self/exe failed");

    if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
        errx(EXIT_FAILURE, "elf_begin() failed: %s.", elf_errmsg(-1));

    ek = elf_kind(e);
    if (ek != ELF_K_ELF)
        errx(EXIT_FAILURE, "self failed: not an elf file");

    dump_gotplt(e);

    return 0;
}
/* vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 */
